package com.example.basic.utils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.example.basic.exception.ServiceException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.gson.Gson;
import okhttp3.*;
import org.apache.commons.collections4.MapUtils;
import org.apache.http.*;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.*;

/**
 * 网络请求相关的工具类
 * 基于OkHttp3
 * http://square.github.io/okhttp/
 *
 * @author tds
 */
public class HttpUtils {
    /**
     *
     */
    private static final OkHttpClient okHttpClient = new OkHttpClient();

    /**
     *
     */
    public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");

    /**
     *
     */
    public static final MediaType XML = MediaType.parse("application/xml; charset=utf-8");

    private final static Logger logger = LoggerFactory.getLogger(HttpUtils.class);

    /**
     * 执行网络请求
     *
     * @param request Request
     * @return Response
     * @throws IOException IOException
     */
    public static Response execute(Request request) throws IOException {
        return okHttpClient.newCall(request).execute();
    }

    /**
     * 执行网络请求，自带回调实现
     *
     * @param request  Request
     * @param callback Callback
     */
    public static void enqueue(Request request, Callback callback) {
        okHttpClient.newCall(request).enqueue(callback);
    }

    /**
     * 获取文本字符串结果
     *
     * @param url 请求链接
     * @return String
     * @throws IOException IOException
     */
    public static String getStringResult(String url) throws IOException {
        Request request = new Request.Builder().url(url).build();
        Response response = execute(request);
        if (response.isSuccessful()) {
            return response.body().string();
        } else {
            throw new IOException("Unexpected code " + response);
        }
    }

    /**
     * get
     *
     * @param url    String
     * @param params Map
     * @return ResponseBody
     * @throws IOException IOException
     */
    public static Response get(String url, Map<String, String> params) throws Exception {
        url = setParmas(url,params,null);
        Request request = new Request.Builder().url(url).get().build();
        return execute(request);
    }

    public static  JSONArray doGet(String url) throws IOException {

        //创建一个可关闭的HttpClient对象

        CloseableHttpClient httpclient = HttpClients.createDefault();

        //创建一个HttpGet的请求对象

        HttpGet httpget = new HttpGet(url);

        //执行请求,相当于postman上点击发送按钮，然后赋值给HttpResponse对象接收

        CloseableHttpResponse httpResponse = httpclient.execute(httpget);

        //拿到Http响应状态码，例如和200,404,500去比较

        int responseStatusCode = httpResponse.getStatusLine().getStatusCode();

        logger.info("response status code -->"+responseStatusCode);

        //把响应内容存储在字符串对象
        String responseString = EntityUtils.toString(httpResponse.getEntity(),"UTF-8");

        //创建Json对象，把上面字符串序列化成Json对象

        JSONArray responseArray = JSONArray.parseArray(responseString);

        logger.info("response array from API-->" + responseArray);

        return responseArray;
    }

    /**
     * get Head
     *
     * @param url    String
     * @param params Map
     * @return ResponseBody
     * @throws IOException IOException
     */
    public static Response getHead(String url, Map<String, String> params,String accessToken) throws Exception {
        url = setParmas(url,params,null);
        Request request = new Request.Builder().url(url).header("X-Access-Token",accessToken).get().build();
        return execute(request);
    }


    /**
     * post
     *
     * @param url    String
     * @param params Map
     * @return Response
     * @throws IOException IOException
     */
    public static Response post(String url, Map<String, String> params) throws IOException {
        // 构建请求内容
        FormBody.Builder builder = new FormBody.Builder();
        if (MapUtils.isNotEmpty(params)) {
            for (String key : params.keySet()) {
                builder.add(key, params.get(key));
            }
        }
        FormBody formBody = builder.build();

        Request request = new Request.Builder()
                .url(url)
                .post(formBody)
                .build();

        return execute(request);
    }    /**
     * post
     *
     * @param url    String
     * @param params Map
     * @return Response
     * @throws IOException IOException
     */
    public static Response postHead(String url, Map<String, String> params,String accessToken) throws IOException {
        // 构建请求内容
        FormBody.Builder builder = new FormBody.Builder();
        if (MapUtils.isNotEmpty(params)) {
            for (String key : params.keySet()) {
                builder.add(key, params.get(key));
            }
        }
        FormBody formBody = builder.build();

        Request request = new Request.Builder()
                .url(url)
                .header("X-Access-Token",accessToken)
                .post(formBody)
                .build();

        return execute(request);
    }

    /**
     * post
     *
     * @param url    String
     * @param params Map
     * @return Response
     * @throws IOException IOException
     */
    public static Response postJson(String url, Map<String, String> params) throws IOException {
        Gson gson = new Gson();
        return postJson(url, gson.toJson(params));
    }

    /**
     * post
     *
     * @param url  String
     * @param json String
     * @return Response
     * @throws IOException IOException
     */
    public static Response postJson(String url, String json) throws IOException {
        RequestBody body = RequestBody.create(JSON, json);
        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();
        return execute(request);
    }

    /**
     * 获取二维码图片
     *
     * @param requestURL    请求地址
     * @param params        请求参数
     * @param encodeCharset 编码字符集
     * @param decodeCharset 解码字符集
     * @return 远程主机响应正文
     * @throws UnsupportedEncodingException
     */
    public static void getQrCodeImag(String requestURL, Map<String, Object> params, File file, String encodeCharset,
                                     String decodeCharset) throws UnsupportedEncodingException {
        // 0.初始化参数
        requestURL = StringUtils.trimToEmpty(requestURL);
        params = (null == params) ? new HashMap<String, Object>() : params;
        encodeCharset = StringUtils.trimToEmpty(encodeCharset);
        encodeCharset = ("".equals(encodeCharset)) ? "UTF-8" : encodeCharset;
        decodeCharset = StringUtils.trimToEmpty(decodeCharset);
        decodeCharset = ("".equals(decodeCharset)) ? "UTF-8" : decodeCharset;
        // 1.设置POST方法
        // 1.1.创建POST方法
        HttpPost postMethod = new HttpPost(requestURL);
        postMethod.setHeader("Accept", "*/*");
        postMethod.addHeader(HTTP.CONTENT_TYPE, "application/json");
        postMethod.setHeader("Connection", "keep-alive");
        // 1.2..设置POST连接以及回应超时时间
        RequestConfig requestConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.BEST_MATCH)
                .setSocketTimeout(50000).setConnectTimeout(50000).setConnectionRequestTimeout(50000).build();
        postMethod.setConfig(requestConfig);
        // 2.设置POST请求参数
        ObjectMapper mapper = new ObjectMapper();
        StringEntity se = null;
        try {
            se = new StringEntity(mapper.writeValueAsString(params));
        } catch (JsonProcessingException e) {
            logger.error(e.getMessage(), e);
        }
        se.setContentType("application/json");
        se.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE, "UTF-8"));
        postMethod.setEntity(se);
        // 3.发送请求
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        // 将最大连接数增加到200
        cm.setMaxTotal(200);
        // 将每个路由基础的连接增加到20
        cm.setDefaultMaxPerRoute(20);
        // 将目标主机的最大连接数增加到50
        // HttpHost localhost = new HttpHost("www.yeetrack.com", 80);
        // cm.setMaxPerRoute(new HttpRoute(localhost), 50);
        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).build();
        // CloseableHttpClient httpClient = HttpClients.createDefault();
        PoolingHttpClientConnectionManager n = new PoolingHttpClientConnectionManager();
        CloseableHttpResponse clientResponse = null;
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            clientResponse = httpClient.execute(postMethod);
            // 3.1.检查返回状态
            StatusLine statusLine = clientResponse.getStatusLine();
            if (statusLine.getStatusCode() >= 300) {
                throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase());
            } // END IF
            // 3.2.检查返回实体
            HttpEntity entity = clientResponse.getEntity();
            if (entity == null) {
                throw new ClientProtocolException("远程访问没有数据返回");
            } else if (entity.getContentLength() > 2147483647L) {
                throw new IllegalArgumentException("返回数据量太大，无法进行缓存");
            }
            //String jsonContent = HttpClientUtil.httpEntityToString( entity, decodeCharset );
            inputStream = entity.getContent();

            outputStream = new FileOutputStream(file);
            int len = 0;
            byte[] buf = new byte[1024];
            while ((len = inputStream.read(buf, 0, 1024)) != -1) {
                outputStream.write(buf, 0, len);
            }
            outputStream.flush();
        } catch (ConnectTimeoutException cte) {
            logger.error("与[" + requestURL + "]连接超时,自动返回空字符串");
        } catch (SocketTimeoutException ste) {
            logger.error("与[" + requestURL + "]读取超时,自动返回空字符串");
        } catch (HttpResponseException e) {
            logger.error(requestURL + "访问失败,状态码为" + e.getStatusCode() + ",原因为" + e.getMessage() + ",堆栈信息如下:");
        } catch (IllegalArgumentException e) {
            logger.error(requestURL + "访问失败,原因为" + e.getMessage() + ",堆栈信息如下:");
        } catch (IOException e) {
            logger.error(requestURL + "访问失败,原因为" + e.getMessage() + ",堆栈信息如下:");
        } catch (Exception e) {
            logger.error(requestURL + "]通信过程中发生异常,堆栈信息如下:" + e);
        } finally {
            if (null != clientResponse) {
                try {
                    clientResponse.close();
                } catch (IOException e) {
                    logger.error("关闭与[" + requestURL + "]的通信时发生异常,堆栈信息如下:" + e);
                }
            } // END IF
        }
    }

    /**
     * 获取token
     *
     * @param requestURL    请求地址
     * @param params        请求参数
     * @param encodeCharset 编码字符集
     * @param decodeCharset 解码字符集
     * @return 远程主机响应正文
     */
    public static String getAccessToken(String requestURL, Map<String, Object> params, String encodeCharset,
                                        String decodeCharset) {
        // 0.初始化参数
        String accessToken = null;
        requestURL = StringUtils.trimToEmpty(requestURL);
        params = (null == params) ? new HashMap<String, Object>() : params;
        encodeCharset = StringUtils.trimToEmpty(encodeCharset);
        encodeCharset = ("".equals(encodeCharset)) ? "UTF-8" : encodeCharset;
        decodeCharset = StringUtils.trimToEmpty(decodeCharset);
        decodeCharset = ("".equals(decodeCharset)) ? "UTF-8" : decodeCharset;
        // 1.设置POST方法
        // 1.1.创建POST方法
        HttpPost postMethod = new HttpPost(requestURL);
        postMethod.setHeader("Accept", "*/*");
        // postMethod.setHeader( "Content-Type",
        // "application/x-www-form-urlencoded; charset=UTF-8" );
        postMethod.setHeader("Connection", "keep-alive");
        // 1.2..设置POST连接以及回应超时时间
        RequestConfig requestConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.BEST_MATCH)
                .setSocketTimeout(50000).setConnectTimeout(50000).setConnectionRequestTimeout(50000).build();
        postMethod.setConfig(requestConfig);
        // 2.设置POST请求参数
        List<NameValuePair> formParams = new ArrayList<NameValuePair>();
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            formParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString()));
        }
        // 3.发送请求
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        // 将最大连接数增加到200
        cm.setMaxTotal(200);
        // 将每个路由基础的连接增加到20
        cm.setDefaultMaxPerRoute(20);
        // 将目标主机的最大连接数增加到50
        // HttpHost localhost = new HttpHost("www.yeetrack.com", 80);
        // cm.setMaxPerRoute(new HttpRoute(localhost), 50);
        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).build();
        // CloseableHttpClient httpClient = HttpClients.createDefault();
        PoolingHttpClientConnectionManager n = new PoolingHttpClientConnectionManager();
        CloseableHttpResponse clientResponse = null;
        try {
            HttpEntity formEntity = new UrlEncodedFormEntity(formParams, encodeCharset);
            postMethod.setEntity(formEntity);
            clientResponse = httpClient.execute(postMethod);
            // 3.1.检查返回状态
            StatusLine statusLine = clientResponse.getStatusLine();
            if (statusLine.getStatusCode() >= 300) {
                throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase());
            } // END IF
            // 3.2.检查返回实体
            HttpEntity entity = clientResponse.getEntity();
            if (entity == null) {
                throw new ClientProtocolException("远程访问没有数据返回");
            } else if (entity.getContentLength() > 2147483647L) {
                throw new IllegalArgumentException("返回数据量太大，无法进行缓存");
            }
            // 3.3.解析实体内容
            String jsonContent = HttpUtils.httpEntityToString(entity, decodeCharset);
            // 返回数据示例:jsonContent={"access_token":"61yolkrTnbCPA3Xb1KqpVf7wtr4WyntenDH5Avdx3gNx_UDoXDsxJ9721unpRELrqsar4wGEP5gsM0p-JZRDmM7vTsKzbcUwKORSV_J4LOwNKWaAGAWTF","expires_in":7200}
            ObjectMapper objectMapper = new ObjectMapper();
            Map<String, String> map = objectMapper.readValue(jsonContent, Map.class);
            accessToken = map.get("access_token");
        } catch (ConnectTimeoutException cte) {
            logger.error("与[" + requestURL + "]连接超时,自动返回空字符串");
        } catch (SocketTimeoutException ste) {
            logger.error("与[" + requestURL + "]读取超时,自动返回空字符串");
        } catch (HttpResponseException e) {
            logger.error(
                    "" + requestURL + "访问失败,状态码为" + e.getStatusCode() + ",原因为" + e.getMessage() + ",堆栈信息如下:");
        } catch (IllegalArgumentException | IOException e) {
            logger.error("" + requestURL + "访问失败,原因为" + e.getMessage() + ",堆栈信息如下:");
        } catch (Exception e) {
            logger.error("与[" + requestURL + "]通信过程中发生异常,堆栈信息如下:" + e);
        } finally {
            if (null != clientResponse) {
                try {
                    clientResponse.close();
                } catch (IOException e) {
                    logger.error("关闭与[" + requestURL + "]的通信时发生异常,堆栈信息如下:" + e);
                }
            } // END IF
        }
        return accessToken;
    }

    /**
     * 将httpEntity内容转为字符串
     *
     * @param entity        httpEntity实体
     * @param decodeCharset 指定解码字符
     * @return
     * @throws IOException
     */
    public static String httpEntityToString(HttpEntity entity, String decodeCharset)
            throws IllegalStateException, IOException {
        decodeCharset = StringUtils.trimToEmpty(decodeCharset);
        decodeCharset = ("".equals(decodeCharset)) ? "UTF-8" : decodeCharset;
        StringBuffer content = new StringBuffer();
        Reader reader = null;
        InputStream instream = null;
        try {
            instream = entity.getContent();
            if (null == instream) {
                content = null;
            } else {
                // 1.选择解码字符集
                ContentType contentType = ContentType.getOrDefault(entity);
                Charset charset = ("".equals(decodeCharset)) ? null : Charset.forName(decodeCharset);
                charset = (null == charset) ? ((null == contentType) ? null : contentType.getCharset()) : charset;
                charset = (null == charset) ? HTTP.DEF_CONTENT_CHARSET : charset;
                // 2.读取内容
                reader = new InputStreamReader(instream, charset);
                BufferedReader bufferedReader = new BufferedReader(reader);
                String line = null;
                while ((line = bufferedReader.readLine()) != null) {
                    content.append(line);
                }
                bufferedReader.close();
            }
        } catch (IllegalStateException e) {
            throw e;
        } catch (IOException e) {
            throw e;
        } finally {
            if (null != reader) {
                try {
                    reader.close();
                } catch (Exception e) {
                    logger.error(e.getMessage(), e);
                }
            } // END IF
        }

        return content.toString();
    }

    /**
     * 发送xml
     * @param url
     * @param xmlObj
     * @return
     */
    public static String sendPost(String url, String xmlObj){
        RequestBody body=RequestBody.create(XML,xmlObj);
        Request requestOk = new Request.Builder()
                .url(url)
                .post(body)
                .build();

        Response response;
        String result="";
        try {
            response = new OkHttpClient().newCall(requestOk).execute();
            result = response.body().string();
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return result;
    }

    /**
     * 拼接参数
     *
     * @param url   需要拼接参数的url
     * @param map   参数
     * @param charset 编码格式
     */
    public static String setParmas(String url, Map<String, String> map, String charset) throws Exception {
        String result = org.apache.commons.lang3.StringUtils.EMPTY;
        boolean hasParams = false;
        if (org.apache.commons.lang3.StringUtils.isNotEmpty(url)) {
            StringBuilder builder = new StringBuilder();
            for (Map.Entry<String, String> entry : map.entrySet()) {
                String key = entry.getKey().trim();
                String value = entry.getValue().trim();
                if (hasParams) {
                    builder.append("&");
                } else {
                    hasParams = true;
                }
                if (org.apache.commons.lang3.StringUtils.isNotEmpty(charset)) {
                    builder.append(key).append("=").append(URLEncoder.encode(value, charset));
                } else {
                    builder.append(key).append("=").append(value);
                }
            }
            result = builder.toString();
        }

        URL u = new URL(url);
        if (org.apache.commons.lang3.StringUtils.isEmpty(u.getQuery())) {
            if (url.endsWith("?")) {
                url += result;
            } else {
                url = url + "?" + result;
            }
        } else {
            if (url.endsWith("&")) {
                url += result;
            } else {
                url = url + "&" + result;
            }
        }
        return url;
    }

    public static Boolean sendPost(String url,String startPoint, String endPoint, String taskRecordId) throws IOException {
        if(StringUtils.emptyParams(url)){
            url = "script-api/createTask";
        }

        HttpClient httpClient = HttpClientBuilder.create().setDefaultRequestConfig(RequestConfig.custom().setConnectTimeout(5000).setSocketTimeout(5000).build()).build();
        HttpPost httpPost = new HttpPost("http://172.16.2.130:8080/"+url);
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectMapper objectMapper = new ObjectMapper();
        ObjectNode rootNode = objectMapper.createObjectNode();
        rootNode.put("startPoint",startPoint);
        rootNode.put("endPoint",endPoint);
        rootNode.put("taskRecordId",taskRecordId);
        objectMapper.writeValue(bos, rootNode);
        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        //执行http请求
        HttpResponse response = httpClient.execute(httpPost);
        //获取请求状态
        int statusCode = response.getStatusLine().getStatusCode();
        logger.info("调度下发状态:"+statusCode);
        //返回结果
        String result = EntityUtils.toString(response.getEntity(), "UTF-8");
        logger.info("调度下发结果:"+result);
        if (statusCode == 200) {
            //返回结果
            JSONObject object = JSONObject.parseObject(result);
            if(object.getInteger("code") != 200){
                throw new ServiceException(object.getString("message"));
            }
            //返回结果
            return true;
        }
        return false;
    }
}
